2016 计蒜之道 百度帐号的选取方案 (中等)(kmp、dp)
题意:
$字符串的循环次数:由某个子串形成字符串的最多重复次数$
$给定L\le 10^3的字符串,现从中选取2个不相交的子串,使得2个子串循环次数相同$
$问方法数$
分析:
$根据kmp的nxt数组,字符串的最小Cycle = l-nxt[l]$
$字符串的最大循环次数Times=l \% Cycle ? 1:l/Cycle$
$直接做l次求nxt就可以得到p[l][r]:=s[l\cdots r]子串的循环次数$
$预处理f[i][p]:=以i开头,且循环次数为p的子串个数$
$然后我们枚举左边的这个子串s[l][r],累加右边suf[r+1][p[l][r]]就可以了$
$预处理复杂度O(n^2),dp也是O(n^2),总复杂度O(n^2)$
代码:
//
// Created by TaoSama on 2016-06-05
// Copyright (c) 2016 TaoSama. All rights reserved.
//
#pragma comment(linker, "/STACK:102400000,102400000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>
using namespace std;
#define pr(x) cout << #x << " = " << x << " "
#define prln(x) cout << #x << " = " << x << endl
const int N = 1e3 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;
char s[N];
int n, nxt[N], p[N][N];
typedef long long LL;
LL suf[N][N];
void getNxt(int st) {
nxt[st] = st - 1;
for(int i = st, j = st - 1; i < n;) {
if(j == st - 1 || s[i] == s[j]) nxt[++i] = ++j;
else j = nxt[j];
}
}
int getTimes(int l, int r) {
int len = r - l, cycle = len - (nxt[r] - l);
return len % cycle ? 1 : len / cycle;
}
int main() {
#ifdef LOCAL
freopen("C:\\Users\\TaoSama\\Desktop\\in.txt", "r", stdin);
// freopen("C:\\Users\\TaoSama\\Desktop\\out.txt","w",stdout);
#endif
ios_base::sync_with_stdio(0);
clock_t _ = clock();
while(scanf("%s", s) == 1) {
n = strlen(s);
memset(suf, 0, sizeof suf);
for(int i = 0; i < n; ++i) {
getNxt(i);
for(int j = i; j < n; ++j) {
p[i][j] = getTimes(i, j + 1);
++suf[i][p[i][j]];
}
}
for(int i = n - 1; ~i; --i)
for(int j = 1; j <= n; ++j)
suf[i][j] += suf[i + 1][j];
LL ans = 0;
for(int i = 0; i < n; ++i)
for(int j = i; j < n; ++j)
ans += suf[j + 1][p[i][j]];
printf("%lld\n", ans);
}
#ifdef LOCAL
printf("\nTime cost: %.2fs\n", 1.0 * (clock() - _) / CLOCKS_PER_SEC);
#endif
return 0;
}